﻿//
// Copyright 2009-2011 by Burt Harris
// Licensed under the Microsoft Public License (Ms-PL)
// See: http://www.codeplex.com/yaml/license
//

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;

namespace YaTools.Yaml
{
    public sealed partial class Dump
    {
        public abstract partial class Decorator
        {
            class Preprocessor : Decorator
            {
                private readonly Dictionary<object, int> _objectCounts;
                private readonly Dictionary<Type, int> _typeCounts;

                public Preprocessor()
                {
                    Contract.Ensures(this._typeCounts != null);
                    Contract.Ensures(this._objectCounts != null);

                    this._typeCounts = new Dictionary<Type, int>(
                        ReferenceEqualityComparer<Type>.Instance);

                    this._objectCounts = new Dictionary<object, int>(
                        ReferenceEqualityComparer<object>.Instance);
                }

                public Dictionary<Type, int> TypeCounts
                {
                    get { return _typeCounts; }
                }

                // ReSharper disable UnusedMember.Local
                [ContractInvariantMethod]
                private void ObjectInvariants()
                {
                    Contract.Invariant(this._typeCounts != null);
                    Contract.Invariant(this._objectCounts != null);
                }

                // ReSharper restore UnusedMember.Local

                /// <summary>
                /// Generates a dictionary to hold the anchor names for objects with multiple references
                /// </summary>
                /// <returns>A dictionary keyed by object reference and with null strings as values.</returns>
                /// <remarks>
                /// The caller is responsible for assigning anchor names as it encounters the objects, and 
                /// setting them in this dictionary, then using those name for aliases where needed.
                /// </remarks>
                public Dictionary<object, string> GenerateObjectAnchors()
                {
                    var result
                        = new Dictionary<object, string>(
                            ReferenceEqualityComparer<object>.Instance);

                    // Now generate the anchors
                    foreach (var pair in _objectCounts)
                        if (pair.Value > 1)
                            result.Add(pair.Key, null);

                    return result;
                }


                public override bool PresentNode(Dump dump, object value)
                {
                    if (value == null) return false;
                    if (value is string) return true;

                    Type type = value.GetType();

                    if (type.IsValueType)
                    {
                        CountType(type);
                        return true;
                    }

                    int i;
                    if (_objectCounts.TryGetValue(value, out i))
                    {
                        _objectCounts[value] = i + 1; // Repeat visit
                        return false;
                    }
                    _objectCounts[value] = 1;
                    CountType(type);
                    return true;
                }

                public override void Preprocess(Dump dump, object value)
                {
                }

                public override string HandleForTag(string tag)
                {
                    return tag;
                }

                private void CountType(Type type)
                {
                    Contract.Requires(type != null);

                    int i;
                    if (_typeCounts.TryGetValue(type, out i))
                        _typeCounts[type] = i + 1;
                    else
                        _typeCounts[type] = 1;
                }


            }
        }
    }
}